package com.ficus.road.maintain.controller;

import com.alibaba.fastjson.JSON;
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.model.OSSObject;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.ficus.road.maintain.authority.dto.RoleDTO;
import com.ficus.road.maintain.authority.service.RoleService;
import com.ficus.road.maintain.authority.service.UserService;
import com.ficus.road.maintain.authority.utils.SecurityUtils;
import com.ficus.road.maintain.authority.vo.LoginResponse;
import com.ficus.road.maintain.core.model.*;
import com.ficus.road.maintain.core.model.oss.AliyunOssResult;
import com.ficus.road.maintain.core.utils.JsonUtils;
import com.ficus.road.maintain.core.utils.QueryFilterCommonConverter;
import com.ficus.road.maintain.dao.CarDao;
import com.ficus.road.maintain.dao.FrontConfigDao;
import com.ficus.road.maintain.dao.HitDao;
import com.ficus.road.maintain.dto.StatusStatisticRequest;
import com.ficus.road.maintain.dto.StatusStatisticResponse;
import com.ficus.road.maintain.operationlog.annotation.OpLog;
import com.ficus.road.maintain.service.HitService;
import com.ficus.road.maintain.service.ZhonghaiClient;
import com.ficus.road.maintain.util.AliyunOSSUtil;
import com.ficus.road.maintain.util.BaiduMapClient;
import com.ficus.road.maintain.util.ImageUtil;
import com.ficus.road.maintain.util.MyDateUtil;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.util.CollectionUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import javax.validation.constraints.Min;
import java.util.*;
import java.util.stream.Collectors;


/**
 * @Author hao.zhou1
 * @Since 2021/4/7 下午6:47
 */
@RestController
@Slf4j
public class HitController {
    @Value("${hit-config.pinned-ip1}")
    private String PinnedIp1;
    @Value("${hit-config.pinned-ip2}")
    private String PinnedIp2;
    @Autowired
    private HitService hitService;
    @Value("${hit-config.history-record-num:10}")
    private int RECORD_NUM_DEFAULT;
    @Value("${delay-time-seconds}")
    private int delayTime;
    @Value("${mockdata}")
    private boolean mockdata;
    @Autowired
    private ZhonghaiClient zhonghaiClient;
    @Autowired
    private BaiduMapClient baiduMapClient;
    @Autowired
    private HitDao hitDao;
    @Autowired
    private CarDao carDao;
    @Autowired
    private OSSClient ossClient;
    @Autowired
    private AliyunOSSUtil aliyunOSSUtil;
    @Autowired
    private UserService userService;
    @Autowired
    private RoleService roleService;
    @Autowired
    private FrontConfigDao frontConfigDao;

    /**
     * hit查询条件转换为query文件
     *
     * @param filter 　原始query对象
     * @return
     * @throws IllegalAccessException
     */
    protected static Query toHitQuery(HitQueryFilterDTO filter) throws IllegalAccessException {
        List<Criteria> criteriaList = new ArrayList<>();

        // 如果是审核页面，不加其他限制
        if (!QueryFilterCommonDTO.isAllFieldNUll(filter.getConfirmation()) && filter.getConfirmation().getHashEq() != null && filter.getConfirmation().getHashEq().equals(Confirmation.ConfirmationStatus.TO_BE_CONFIRMED.toString())) {
            criteriaList.add(QueryFilterCommonConverter.toCriteria("attrs.confirmation.status", filter.getConfirmation()));
            //病害来源
            if (!QueryFilterCommonDTO.isAllFieldNUll(filter.getSource())) {
                criteriaList.add(QueryFilterCommonConverter.toCriteria("attrs.source", filter.getSource()));
            }
            Criteria criteria = new Criteria();
            if (!criteriaList.isEmpty()) {
                criteria.andOperator(criteriaList.toArray(new Criteria[criteriaList.size()]));
            }
            return new Query(criteria);
        }
        QueryFilterCommonDTO statusFilter = new QueryFilterCommonDTO();
        statusFilter.setNotEq("DELETE");
        criteriaList.add(QueryFilterCommonConverter.toCriteria("attrs.confirmation.status", statusFilter));
        if (!QueryFilterCommonDTO.isAllFieldNUll(filter.getConfirmation())) {
            criteriaList.add(QueryFilterCommonConverter.toCriteria("attrs.confirmation.status", filter.getConfirmation()));
        }
        if (!QueryFilterCommonDTO.isAllFieldNUll(filter.getScene())) {
            criteriaList.add(QueryFilterCommonConverter.toCriteria("scene", filter.getScene()));
        }
        //时间筛选
        if (!QueryFilterCommonDTO.isAllFieldNUll(filter.getHitTime())) {
            Criteria criteria = Criteria.where("first_hit_time");
            if (Objects.nonNull(filter.getHitTime().getHashLt())) {
                criteria.lt(filter.getHitTime().getHashLt());
            }
            if (Objects.nonNull(filter.getHitTime().getHashGt())) {
                criteria.gt(filter.getHitTime().getHashGt());
            }
            if (Objects.nonNull(filter.getHitTime().getHashLte())) {
                criteria.lte(filter.getHitTime().getHashLte());
            }
            if (Objects.nonNull(filter.getHitTime().getHashGte())) {
                criteria.gte(filter.getHitTime().getHashGte());
            }
            criteriaList.add(criteria);
        }
        //处置撞状态
        if (!QueryFilterCommonDTO.isAllFieldNUll(filter.getStatus())) {
            criteriaList.add(QueryFilterCommonConverter.toCriteria("attrs.status", filter.getStatus()));
        }
        //处置公司
        if (!QueryFilterCommonDTO.isAllFieldNUll(filter.getHandleCompanyCode())) {
            criteriaList.add(QueryFilterCommonConverter.toCriteria("attrs.handle_company_code", filter.getHandleCompanyCode()));
        }
        //闭环类型
        if (!QueryFilterCommonDTO.isAllFieldNUll(filter.getLoop())) {
            criteriaList.add(QueryFilterCommonConverter.toCriteria("attrs.loop", filter.getLoop()));
        }
        //病害来源
        if (!QueryFilterCommonDTO.isAllFieldNUll(filter.getSource())) {
            criteriaList.add(QueryFilterCommonConverter.toCriteria("attrs.source", filter.getSource()));
        }
        //病害类型
        if (!QueryFilterCommonDTO.isAllFieldNUll(filter.getType())) {
            criteriaList.add(QueryFilterCommonConverter.toCriteria("type", filter.getType()));
        }
        if (!QueryFilterCommonDTO.isAllFieldNUll(filter.getCarIds())) {
            criteriaList.add(QueryFilterCommonConverter.toCriteria("attrs.car_id", filter.getCarIds()));
        }
        Criteria criteria = new Criteria();
        if (!criteriaList.isEmpty()) {
            criteria.andOperator(criteriaList.toArray(new Criteria[criteriaList.size()]));
        }
        return new Query(criteria);
    }

    @RequestMapping(value = "/hits",
            produces = {"application/json"},
            consumes = {"application/json"},
            method = RequestMethod.POST)
    public ResponseEntity<IdResult> createHit(@Valid @RequestBody Hit hit) {
        IdResult idResult = new IdResult();
        String id = hitService.createHit(hit);
        idResult.setId(id);
        //回传一个序列化的id
        return new ResponseEntity<>(idResult, HttpStatus.OK);
    }

    //删除一个告警
    @RequestMapping(value = "/hits/{id}",
            produces = {"application/json"},
            consumes = {"application/json"},
            method = RequestMethod.DELETE)
    public ResponseEntity<IdResult> deleteHit(@PathVariable("id") String id) {
        hitService.delete(id);
        IdResult idResult = new IdResult();
        idResult.setId(id);
        return new ResponseEntity<>(idResult, HttpStatus.OK);
    }

    @OpLog(description = "中海回调")
    @RequestMapping(value = "/hits/updateStatus/{id}",
            method = RequestMethod.POST)
    public ResponseEntity<IdResult> updateHitStatus(@PathVariable("id") String id, @Valid @RequestBody UpdateHitStatusRequest updateHitStatusRequest) {
        log.info("收到更新状态请求id:{}, 请求体: {}", id, JSON.toJSONString(updateHitStatusRequest));
        hitService.updateHitStatus(id, updateHitStatusRequest);
        IdResult idResult = new IdResult();
        idResult.setId(id);
        return new ResponseEntity<>(idResult, HttpStatus.OK);
    }

    @RequestMapping(value = "/zh/sendDisease/{id}",
            method = RequestMethod.GET)
    public ResponseEntity<String> testSend(@PathVariable("id") String id) {
        Map map = zhonghaiClient.getDisease(id);
        String client = zhonghaiClient.client(map);
        return new ResponseEntity<>(client, HttpStatus.OK);
    }

    // todo: 前端轮询
    @RequestMapping(value = "/hits/statistic/type",
            method = RequestMethod.GET)
    public ResponseEntity<List<DiseaseTypeCount>> statisticByType() {
        if (mockdata) {
            FrontConfig frontConfig = frontConfigDao.fetch("statisticByType");
            String data = frontConfig.getData();
            List<DiseaseTypeCount> diseaseTypeCounts = JSON.parseArray(data, DiseaseTypeCount.class);
            return new ResponseEntity<>(diseaseTypeCounts, HttpStatus.OK);
        } else {
            List<DiseaseTypeCount> diseaseTypeCounts = hitService.statisticByType();
            return new ResponseEntity<>(diseaseTypeCounts, HttpStatus.OK);
        }
    }

    //调用中海的接口，复核病害
    @RequestMapping(value = "/zh/reviewResult/{id}/{resultNum}",
            method = RequestMethod.GET)
    public ResponseEntity<String> reviewResult(@PathVariable("id") String id, @PathVariable("resultNum") String resultNum) {
        try {
            String remarks = "";
            if (resultNum.equals("1")) {
                remarks = "审核通过";
            } else if (resultNum.equals("2")) {
                remarks = "处理未完成";
            }
            zhonghaiClient.doReviewResult(id, resultNum, remarks, "");
        } catch (Exception e) {
            log.error("reviewResult met exception", e);
        }
        return new ResponseEntity<>("success", HttpStatus.OK);
    }

    //获取一个告警信息
    @RequestMapping(value = "/hits/{id}",
            method = RequestMethod.GET)
    public ResponseEntity<Hit> getHit(@PathVariable("id") String id) {
        Hit hit = hitService.getHit(id);
        hit.setList(null);
        return new ResponseEntity<>(hit, HttpStatus.OK);
    }

    //月度病害统计
    @RequestMapping(value = "/hits/disease/mensalStatistic",
            produces = {"application/json"},
            consumes = {"application/json"},
            method = RequestMethod.POST)
    public ResponseEntity<MensalDiseaseStatisticResponse> statisticOrderDisease(@RequestBody IdResult id) {
        MensalDiseaseStatisticResponse mensalDiseaseStatisticResponse = hitService.queryMensalDiseaseStatistic(id.getId());
        return new ResponseEntity<>(mensalDiseaseStatisticResponse, HttpStatus.OK);
    }


    //这个接口只给审核用
    @RequestMapping(value = "/hits/{id}",
            produces = {"application/json"},
            consumes = {"application/json"},
            method = RequestMethod.PATCH)
    public ResponseEntity<IdResult> updateHit(@PathVariable("id") String id, @Valid @RequestBody Hit hit) throws JsonProcessingException {
        IdResult idResult = new IdResult();
        idResult.setId(id);
        hit.setId(id);
        Hit existHit = hitService.getHit(id);
        //JsonNode:用于增量修改
        JsonNode mainNode = JsonUtils.objectMapper.convertValue(existHit, JsonNode.class);
        JsonNode updateNode = JsonUtils.objectMapper.convertValue(hit, JsonNode.class);
        JsonNode merge = JsonUtils.merge(mainNode, updateNode);
        Hit mergeHit = JsonUtils.objectMapper.treeToValue(merge, Hit.class);
        mergeHit.setLastModificationTime(System.currentTimeMillis() / 1000);
        //推送逻辑移到定时任务

        // 如果是复核逻辑, 需要推送到中海
        if (existHit.getAttrs().getStatus().equals(HitStatusEnum.COMPLETE) && existHit.getAttrs().getConfirmation().getStatus().equals(Confirmation.ConfirmationStatus.TO_BE_CONFIRMED)) {
            if (mergeHit.getAttrs().getConfirmation().getStatus().equals(Confirmation.ConfirmationStatus.CORRECT)) {
                try {
                    String sceneImageUrl = hit.getList().get(0).getAttrs().getSceneImage();
                    OSSObject ossObject = aliyunOSSUtil.getFileByUrl(sceneImageUrl);
                    byte[] bytes = ossObject.getObjectContent().readAllBytes();
                    String originImageBase64 = ImageUtil.bytes2Base64(bytes);
                    List<HitSubject> subjects = hit.getList().get(0).getSubjects();
                    List<Bbox> bboxList = subjects.stream().map(item -> item.getBbox()).collect(Collectors.toList());
                    List<RelBbox> relBboxList = subjects.stream().map(item -> item.getRelBbox()).collect(Collectors.toList());
                    byte[] drawFrameImageBytes = ImageUtil.drawFrame(originImageBase64, bboxList, relBboxList);
                    String[] split = sceneImageUrl.split("aliyuncs.com");
                    String reviewPic = sceneImageUrl;
                    if (split.length == 2) {
                        String originCompleteName = split[1];
                        String[] originName = originCompleteName.split(".");
                        AliyunOssResult drawFrameImageResult = aliyunOSSUtil.uploadImg(originName + "-draw.jpg", drawFrameImageBytes);
                        if (drawFrameImageResult.getCode() == 200) {
                            reviewPic = drawFrameImageResult.getUrl();
                        } else {
                            log.info("upload image failed, hitId: {}, hitImage64:{}", hit.getId(), originImageBase64);
                        }
                    }
                    zhonghaiClient.doReviewResult(id, "2", "复核未通过", reviewPic);
                    mergeHit.getAttrs().setStatus(HitStatusEnum.CONFIRMFAILED);
                    mergeHit.getList().get(0).getAttrs().setStatus(HitStatusEnum.CONFIRMFAILED);
                } catch (Exception e) {
                    log.error("review Hit met Exception: ", e);
                }
            } else {
                try {
                    mergeHit.getAttrs().setStatus(HitStatusEnum.CONFIRMED);
                    mergeHit.getList().get(0).getAttrs().setStatus(HitStatusEnum.CONFIRMED);
                    zhonghaiClient.doReviewResult(id, "1", "复核通过", "");
                } catch (Exception e) {
                    log.error("review Hit met Exception: ", e);
                }
            }
        }
        hitDao.saveOrUpdate(mergeHit);
        return new ResponseEntity<>(idResult, HttpStatus.OK);
    }


    @RequestMapping(value = "/hits/reConfirm/{id}",
            produces = {"application/json"},
            consumes = {"application/json"},
            method = RequestMethod.PATCH)
    public ResponseEntity<IdResult> updateHit(@PathVariable("id") String id, @Valid @RequestBody ConfirmHitRequest confirmHitRequest) throws JsonProcessingException {
        hitService.reConfirm(id, confirmHitRequest);
        IdResult idResult = new IdResult();
        idResult.setId(id);
        return new ResponseEntity<>(idResult, HttpStatus.OK);
    }

    //按指定条件查询病害
    // todo: 前端轮询
    @RequestMapping(value = "/hits/query",
            produces = {"application/json"},
            consumes = {"application/json"},
            method = RequestMethod.POST)
    public ResponseEntity<List<Hit>> queryHit(@Min(0) @Valid @RequestParam(defaultValue = "0") Integer start,
                                              @Min(1) @Valid @RequestParam(defaultValue = "1000") Integer limit,
                                              @RequestParam(required = false) String cursor,
                                              @Valid @RequestBody HitQueryRequestDTO hitQueryRequest)
            throws IllegalAccessException {
        Query query = toHitQuery(hitQueryRequest.getFilter());
        UserDetails currentUser = SecurityUtils.getCurrentUser();
        LoginResponse loginResponse = userService.queryLoginInfoByUsername(currentUser.getUsername());
        List<RoleDTO> roleDTOS = roleService.queryRolesById(loginResponse.getRoleIdList());
        Set<String> roleNames = roleDTOS.stream().map(RoleDTO::getRoleName).collect(Collectors.toSet());
        if (!roleNames.contains("管理员") && !roleNames.contains("道运中心") && !roleNames.contains("市道运局")) {
            query.addCriteria(Criteria.where("attrs.car_id").in(loginResponse.getCarIdList()));
        }
        List<Hit> hits = new ArrayList<>();
        if(start == 0){
            query.skip(start).limit(limit-2);
            Criteria criteria = Criteria.where("_id").in(PinnedIp1,PinnedIp2);
            Query query1 = new Query(criteria);
            hits = hitService.queryHits(query1);
        }else {
            query.skip(start).limit(limit);
        }
        hits.addAll(hitService.queryHits(query));
        if(start == 0){

        }
        //历史病害记录清空，前端不需要这个字段
//        hits.forEach(hit -> hit.setList(null));
        //根据id查符合条件的总数
        query.fields().include("_id");
        //最多返回一万条数据,防止无限分页
        query.skip(0).limit(10000);
        int total = hitService.countHits(query);
        MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
        Boolean isFinal = Boolean.FALSE;
        if (hits.size() != limit) {
            isFinal = Boolean.TRUE;
        }
        headers.add("X-OneAPI-Exhausted", String.valueOf(isFinal));
        headers.add("X-OneAPI-EstimatedSize", String.valueOf(total));
        return new ResponseEntity<>(hits, headers, HttpStatus.OK);
    }

    //返回满足查询条件的数量
    @RequestMapping(value = "/hits/query/total",
            produces = {"application/json"},
            consumes = {"application/json"},
            method = RequestMethod.POST)
    public ResponseEntity<TotalResult> queryTotal(@Valid @RequestBody HitQueryRequestDTO hitQueryRequest) throws IllegalAccessException {
        Query query = toHitQuery(hitQueryRequest.getFilter());
        query.fields().include("_id");
        query.skip(0).limit(10000);
        int total = hitService.countHits(query);
        TotalResult totalResult = new TotalResult();
        totalResult.setTotal(total);
        return new ResponseEntity<>(totalResult, HttpStatus.OK);
    }

    //返回满足查询条件的数量
    // todo:前端轮询
    @RequestMapping(value = "/hits/scenes/statistic",
            method = RequestMethod.POST)
    public ResponseEntity<SceneStatisticResponse> getScenesStatistic(@RequestBody SceneFilter filter) {
//        SceneStatisticResponse response = new SceneStatisticResponse();
//        Query query = new Query();
//        UserDetails currentUser = SecurityUtils.getCurrentUser();
//        LoginResponse loginResponse = userService.queryLoginInfoByUsername(currentUser.getUsername());
//        List<RoleDTO> roleDTOS = roleService.queryRolesById(loginResponse.getRoleIdList());
//        Set<String> roleNames = roleDTOS.stream().map(RoleDTO::getRoleName).collect(Collectors.toSet());
//        if (!roleNames.contains("管理员") && !roleDTOS.contains("道运中心")) {
//            query.addCriteria(Criteria.where("attrs.car_id").in(loginResponse.getCarIdList()));
//        }
//        Date monthAgo = DateUtil.getNdaysMorning(30);
//        query.addCriteria(Criteria.where("attrs.confirmation.status").is(Confirmation.ConfirmationStatus.CORRECT.toString()));
//        query.addCriteria(Criteria.where("hit_time").gt(monthAgo.getTime() / 1000));
//        query.fields().include("scene");
//        List<Hit> hits = hitService.queryHits(query);
//        Map<String, List<Hit>> collect = hits.stream().collect(Collectors.groupingBy(Hit::getScene));
//        List<Hit> cityList = collect.getOrDefault("CITY", new ArrayList<>());
//        List<Hit> economicList = collect.getOrDefault("ECONOMIC", new ArrayList<>());
//        List<Hit> societyList = collect.getOrDefault("SOCIETY", new ArrayList<>());
//        response.setCityCount(cityList.size());
//        response.setEconomicCount(economicList.size());
//        response.setSocietyCount(societyList.size());
        List<DiseaseTypeCount> diseaseTypeCounts = hitService.statisticByType();
        int city = 0;
        int economic = 0;
        int society = 0;
        if (!CollectionUtils.isEmpty(filter.getCityType())) {
            city = diseaseTypeCounts.stream().filter(item -> filter.getCityType().contains(item.getType())).mapToInt(DiseaseTypeCount::getCount).sum();
        }
        if (!CollectionUtils.isEmpty(filter.getEconomicType())) {
            economic = diseaseTypeCounts.stream().filter(item -> filter.getEconomicType().contains(item.getType())).mapToInt(DiseaseTypeCount::getCount).sum();
        }
        if (!CollectionUtils.isEmpty(filter.getSocietyType())) {
            society = diseaseTypeCounts.stream().filter(item -> filter.getSocietyType().contains(item.getType())).mapToInt(DiseaseTypeCount::getCount).sum();
        }
        SceneStatisticResponse response = SceneStatisticResponse.builder().cityCount(city).economicCount(economic).societyCount(society).build();
        return new ResponseEntity<>(response, HttpStatus.OK);
    }

    @RequestMapping(value = "/hits/getHistoryById/{hitId}",
            method = RequestMethod.GET)
    public ResponseEntity<Hit> queryHist(@PathVariable String hitId) {
        Hit hit = hitService.getHit(hitId);
        return new ResponseEntity<>(hit, HttpStatus.OK);
    }

    //批量查询历史病害记录
    @RequestMapping(value = "/hits/getHistoryByIds",
            method = RequestMethod.POST)
    public ResponseEntity<List<Hit>> queryHistByIds(@Valid @RequestBody List<String> hitIds) {
        Criteria criteria = Criteria.where("id").in(hitIds);
        List<Hit> hits = hitService.queryHits(new Query(criteria));
        if (CollectionUtils.isEmpty(hits)) {
            return new ResponseEntity<>(Lists.newArrayList(), HttpStatus.OK);
        }
        Map<String, Hit> hitMap = hits.stream().collect(Collectors.toMap(Hit::getId, a -> a));
        List<Hit> resultList = hitIds.stream().map(item -> hitMap.get(item)).collect(Collectors.toList());
        return new ResponseEntity<>(resultList, HttpStatus.OK);
    }

    @RequestMapping(value = "/hits/statusStatistic",
            method = RequestMethod.POST)
    public ResponseEntity<List<StatusStatisticResponse>> statusStatistic(@Valid @RequestBody StatusStatisticRequest statusStatisticRequest) {
        List<StatusStatisticResponse> resultList = hitService.statusStatistic(
                Objects.isNull(statusStatisticRequest.getStartTime()) ? MyDateUtil.getFirstDayOfMonth().getTime() / 1000 : statusStatisticRequest.getStartTime(),
                Objects.isNull(statusStatisticRequest.getEndTime()) ? System.currentTimeMillis() / 1000 : statusStatisticRequest.getEndTime(),
                statusStatisticRequest.getTypes());
        return new ResponseEntity<>(resultList, HttpStatus.OK);
    }
}
